package net.qiujuer.genius.ui.widget;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Attr;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Component.EstimateSizeListener;
import ohos.agp.components.ComponentState;
import ohos.agp.components.DragInfo;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;

import net.qiujuer.genius.ui.widget.util.AttrValue;
import net.qiujuer.genius.ui.widget.util.FormatUtils;
import net.qiujuer.genius.ui.widget.util.SizeUtils;
import net.qiujuer.genius.ui.widget.view.Builder;
import net.qiujuer.genius.ui.widget.view.ColorCollector;
import net.qiujuer.genius.ui.widget.view.Indicator;
import net.qiujuer.genius.ui.widget.view.IndicatorType;
import net.qiujuer.genius.ui.widget.view.OnSeekBarChangeListener;
import net.qiujuer.genius.ui.widget.view.SeekParams;
import net.qiujuer.genius.ui.widget.view.TickMarkType;
import net.qiujuer.genius.ui.widget.view.TouchHelper;

import java.math.BigDecimal;
import java.util.Optional;

/**
 * created by zhuangguangquan on 2017/9/1
 * <p>
 * A custom SeekBar on ohos, which can be changed the size ,
 * color , thumb drawable , tick drawable , texts , indicator;
 * also can show an indicator view above SeekBar when seeking.
 * <p>
 * https://github.com/warkiz/IndicatorSeekBar
 * <p>
 * Donation/打赏:
 * If this library is helpful to you ,you can give me a donation by:
 *
 * @see <a href="https://www.paypal.me/BuyMeACupOfTeaThx">ZhuanGuangQuan's Paypal</a>, or
 * @see <a href="https://github.com/warkiz/IndicatorSeekBar/blob/master/app/src/main/res/mipmap-xxhdpi/wechat_pay.png?raw=true">微信支付</a>, or
 * @see <a href="https://github.com/warkiz/IndicatorSeekBar/blob/master/app/src/main/res/mipmap-xxhdpi/alipay.png?raw=true">支付宝</a>
 * <p>
 */

public class AbsSeekBar extends Component implements EstimateSizeListener, Component.TouchEventListener, Component.DraggedListener {
//    HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x01, "IndicatorSeekBar--");
    private static final int THUMB_MAX_WIDTH = 30;
    private static final String FORMAT_PROGRESS = "${PROGRESS}";
    private static final String FORMAT_TICK_TEXT = "${TICK_TEXT}";
    private Context mContext;
    private Paint mStockPaint;//the paint for seek bar drawing
    private Paint mTextPaint;//the paint for mTickTextsArr drawing
    private OnSeekBarChangeListener mOnSeekBarChangeListener;
    private Rect mRect;
    private float mCustomDrawableMaxHeight;//the max height for custom drawable
    private double lastProgress;
    private double mFaultTolerance = -1;//the tolerance for user seek bar touching
    private double mScreenWidth = -1;
    private boolean mClearPadding;
    private SeekParams mSeekParams;//save the params when seeking change.
    //seek bar
    private int mPaddingLeft;
    private int mPaddingRight;
    private int mMeasuredWidth;
    private int mPaddingTop;
    private float mSeekLength;//the total length of seek bar
    private float mSeekBlockLength;//the length for each section part to seek
    private boolean mIsTouching;//user is touching the seek bar
    private float mMax;
    private float mMin;
    private float mProgress;
    private boolean mIsFloatProgress;// true for the progress value in float,otherwise in int.
    private int mScale = 1;//the scale of the float progress.
    private boolean mUserSeekable;//true if the user can seek to change the progress,otherwise only can be changed by setProgress().
    private boolean mOnlyThumbDraggable;//only drag the seek bar's thumb can be change the progress
    private boolean mSeekSmoothly;//seek continuously
    private float[] mProgressArr;//save the progress which at tickMark position.
    private boolean mR2L;//right to left,compat local problem.
    //tick texts
    private boolean mShowTickText;//the palace where the tick text show .
    private boolean mShowBothTickTextsOnly;//show the tick texts on the both ends of seek bar before.
    private int mTickTextsHeight;//the height of text
    private String[] mTickTextsArr;//save the tick texts which at tickMark position.
    private float[] mTickTextsWidth;//save the tick texts width bounds.
    private float[] mTextCenterX;//the text's drawing X anchor
    private float mTickTextY;//the text's drawing Y anchor
    private int mTickTextsSize;
    private Font mTextsTypeface;//the tick texts and thumb texts' typeface
    private int mSelectedTextsColor;//the color for the tick texts those thumb swept.
    private int mUnselectedTextsColor;//the color for the tick texts those thumb haven't reach.
    private int mHoveredTextColor;//the color for the tick texts which below/above thumb.
    private CharSequence[] mTickTextsCustomArray;
    //indicator
    private Indicator mIndicator;//the pop window above the seek bar
    private Color mIndicatorColor;
    private Color mIndicatorTextColor;
    private boolean mIndicatorStayAlways;//true if the indicator didn't dismiss after initial.
    private int mIndicatorTextSize;
    private Component mIndicatorContentView;//the view to replace the raw indicator all view
    private Component mIndicatorTopContentView;//the view to replace the raw indicator content view
    private int mShowIndicatorType;//different indicator type.
    private String mIndicatorTextFormat;
    //tick marks
    private float[] mTickMarksX;//the tickMark's drawing X anchor
    private int mTicksCount;//the num of tickMarks
    private int mUnSelectedTickMarksColor;//the color for the tickMarks those thumb haven't reach.
    private int mSelectedTickMarksColor;//the color for the tickMarks those thumb swept.
    private float mTickRadius;//the tick's radius
    private Element mUnselectTickMarksBitmap;//the drawable bitmap for tick
    private Element mSelectTickMarksBitmap;//the drawable bitmap for tick
    private Element mTickMarksDrawable;
    private int mShowTickMarksType;
    private boolean mTickMarksEndsHide;//true if want to hide the tickMarks which in both side ends of seek bar
    private boolean mTickMarksSweptHide;//true if want to hide the tickMarks which on thumb left.
    private int mTickMarksSize;//the width of tickMark
    //track
    private boolean mTrackRoundedCorners;
    private RectFloat mProgressTrack;//the background track on the thumb start
    private RectFloat mBackgroundTrack;//the background track on the thumb ends
    private int mBackgroundTrackSize;
    private int mProgressTrackSize;
    private Color mBackgroundTrackColor;
    private Color mProgressTrackColor;
    private int[] mSectionTrackColorArray;//save the color for each section tracks.
    private boolean mCustomTrackSectionColorResult;//true to confirm to custom the section track color
    //thumb
    private float mThumbRadius;//the thumb's radius
    private float mThumbTouchRadius;//the thumb's radius when touching
    private Element mThumbBitmap;//the drawable bitmap for thumb
    private int mThumbColor;
    private int mThumbSize;
    private Element mThumbDrawable;
    private Element mPressedThumbBitmap;//the bitmap for pressing status
    private int mPressedThumbColor;//the color for pressing status
    //thumb text
    private boolean mShowThumbText;//the place where the thumb text show .
    private float mThumbTextY;//the thumb text's drawing Y anchor
    private Color mThumbTextColor;
    private boolean mHideThumb;
    private boolean mAdjustAuto;
    private boolean mIndicatorBoolen;

    public AbsSeekBar(Context context) {
        this(context, null);
    }

    public AbsSeekBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
        this.mContext = context;
        initAttrs(mContext, attrSet);
        setEstimateSizeListener(this);
        addDrawTask(drawTask);
        initParams();
        setTouchEventListener(this);
        setDraggedListener(DRAG_HORIZONTAL, this);
    }

    public AbsSeekBar(Context context, AttrSet attrSet, int defStyleAttr) {
        super(context, attrSet, defStyleAttr);
        this.mContext = context;
        initAttrs(mContext, attrSet);
        setEstimateSizeListener(this);
        addDrawTask(drawTask);
        initParams();
        setTouchEventListener(this);
        setDraggedListener(DRAG_HORIZONTAL, this);
    }

    /**
     * if you want a java build, the way like:
     * <p>
     * IndicatorSeekBar
     * .with(getContext())
     * .max(50)
     * .min(10)
     * ...
     * .build();
     */
    public AbsSeekBar(Builder builder) {
        super(builder.context);
        this.mContext = builder.context;
        setEstimateSizeListener(this);
        addDrawTask(drawTask);
        int defaultPadding = SizeUtils.vp2px(mContext, 16);
        setPadding(defaultPadding, getPaddingTop(), defaultPadding, getPaddingBottom());
        this.apply(builder);
        initParams();
        setTouchEventListener(this);
        setDraggedListener(DRAG_HORIZONTAL, this);
    }

    private void initAttrs(Context context, AttrSet attrSet) {
        Builder builder = new Builder(context);
        if (attrSet == null) {
            this.apply(builder);
            return;
        }
        //seekBar
        mMax = AttrValue.get(attrSet, "isb_max", builder.max);
        mMin = AttrValue.get(attrSet, "isb_min", builder.min);
        mProgress = AttrValue.get(attrSet, "isb_progress", builder.progress);
        mIsFloatProgress = AttrValue.get(attrSet, "isb_progress_value_float", builder.progressValueFloat);
        mUserSeekable = AttrValue.get(attrSet, "isb_user_seekable", builder.userSeekable);
        mClearPadding = AttrValue.get(attrSet, "isb_clear_default_padding", builder.clearPadding);
        mOnlyThumbDraggable = AttrValue.get(attrSet, "isb_only_thumb_draggable", builder.onlyThumbDraggable);
        mSeekSmoothly = AttrValue.get(attrSet, "isb_seek_smoothly", builder.seekSmoothly);
        mR2L = AttrValue.get(attrSet, "isb_r2l", builder.r2l);
        //track
        mBackgroundTrackSize = AttrValue.getDimension(attrSet, "isb_track_background_size", builder.trackBackgroundSize);
        mProgressTrackSize = AttrValue.getDimension(attrSet, "isb_track_progress_size", builder.trackProgressSize);
        mBackgroundTrackColor = AttrValue.get(attrSet, "isb_track_background_color", builder.trackBackgroundColor);
        mProgressTrackColor = AttrValue.get(attrSet, "isb_track_progress_color", builder.trackProgressColor);
        mTrackRoundedCorners = AttrValue.get(attrSet, "isb_track_rounded_corners", builder.trackRoundedCorners);
        //thumb
        mThumbSize = AttrValue.getDimension(attrSet, "isb_thumb_size", builder.thumbSize);
        if (attrSet.getAttr("isb_thumb_drawable").isPresent()) {
            mThumbDrawable = attrSet.getAttr("isb_thumb_drawable").get().getElement();
        }
        mAdjustAuto = AttrValue.get(attrSet, "isb_thumb_adjust_auto", true);
        initThumbColor(getAttrColor(attrSet.getAttr("isb_thumb_color"),
            new int[]{ComponentState.COMPONENT_STATE_PRESSED, ComponentState.COMPONENT_STATE_EMPTY}),
            builder.thumbColor);
        //thumb text
        mShowThumbText = AttrValue.get(attrSet, "isb_show_thumb_text", builder.showThumbText);
        mThumbTextColor = AttrValue.get(attrSet, "isb_thumb_text_color", builder.thumbTextColor);
        //tickMarks
        mTicksCount = AttrValue.get(attrSet, "isb_ticks_count", builder.tickCount);
        mShowTickMarksType = AttrValue.get(attrSet, "isb_show_tick_marks_type", builder.showTickMarksType);
        mTickMarksSize = AttrValue.getDimension(attrSet, "isb_tick_marks_size", builder.tickMarksSize);
        initTickMarksColor(getAttrColor(attrSet.getAttr("isb_tick_marks_color"),
            new int[]{ComponentState.COMPONENT_STATE_SELECTED, ComponentState.COMPONENT_STATE_EMPTY}),
            builder.tickMarksColor);
        if (attrSet.getAttr("isb_tick_marks_drawable").isPresent()) {
            mTickMarksDrawable = attrSet.getAttr("isb_tick_marks_drawable").get().getElement();
        }
        mTickMarksSweptHide = AttrValue.get(attrSet, "isb_tick_marks_swept_hide", builder.tickMarksSweptHide);
        mTickMarksEndsHide = AttrValue.get(attrSet, "isb_tick_marks_ends_hide", builder.tickMarksEndsHide);
        //tickTexts
        mShowTickText = AttrValue.get(attrSet, "isb_show_tick_texts", builder.showTickText);
        mTickTextsSize = AttrValue.getDimension(attrSet, "isb_tick_texts_size", builder.tickTextsSize);
        initTickTextsColor(getAttrColor(attrSet.getAttr("isb_tick_texts_color"),
            new int[]{ComponentState.COMPONENT_STATE_HOVERED, ComponentState.COMPONENT_STATE_SELECTED, ComponentState.COMPONENT_STATE_EMPTY}),
            builder.tickTextsColor);
        //indicator
        mShowIndicatorType = AttrValue.get(attrSet, "isb_show_indicator", builder.showIndicatorType);
        mIndicatorColor = AttrValue.get(attrSet, "isb_indicator_color", builder.indicatorColor);
        mIndicatorTextSize = AttrValue.getDimension(attrSet, "isb_indicator_text_size", builder.indicatorTextSize);
        mIndicatorTextColor = AttrValue.get(attrSet, "isb_indicator_text_color", builder.indicatorTextColor);
        mIndicatorBoolen = AttrValue.get(attrSet, "isb_indicatorboolen", builder.indicatorboolen);
    }

    private StateElement getAttrColor(Optional<Attr> attr, int[] states) {
        StateElement mThumbColorState = new StateElement();
        if (attr.isPresent()) {
            String name = attr.get().getStringValue();
            if (name.contains("$graphic:")) {
                Element element = attr.get().getElement();

                mThumbColorState = (StateElement) element;
            } else {
                Color color = attr.get().getColorValue();
                ShapeElement shapeElement = new ShapeElement();
                shapeElement.setRgbColor(RgbColor.fromArgbInt(color.getValue()));
                for (int state : states) {
                    mThumbColorState.addState(new int[]{state}, shapeElement);
                }
            }
        }
        return mThumbColorState;
    }

    public void setOnSeekBarChangeListener(OnSeekBarChangeListener mSeekBarChangeListener) {
        this.mOnSeekBarChangeListener = mSeekBarChangeListener;
    }

    private void initParams() {
        initProgressRangeValue();
        if (mBackgroundTrackSize > mProgressTrackSize) {
            mBackgroundTrackSize = mProgressTrackSize;
        }
        if (mThumbDrawable == null) {
            mThumbRadius = mThumbSize / 2.0f;
            mThumbTouchRadius = mThumbRadius * 1.6f;
        } else {
            mThumbRadius = Math.min(SizeUtils.vp2px(mContext, THUMB_MAX_WIDTH), mThumbSize) / 2.0f;
            mThumbTouchRadius = mThumbRadius;
        }
        if (mTickMarksDrawable == null) {
            mTickRadius = mTickMarksSize / 2.0f;
        } else {
            mTickRadius = Math.min(SizeUtils.vp2px(mContext, THUMB_MAX_WIDTH), mTickMarksSize) / 2.0f;
        }
        mCustomDrawableMaxHeight = Math.max(mThumbTouchRadius, mTickRadius) * 2.0f;
        initStrokePaint();
        measureTickTextsBonds();
        lastProgress = mProgress;

        collectTicksInfo();

        mProgressTrack = new RectFloat();
        mBackgroundTrack = new RectFloat();
        initDefaultPadding();
        initIndicatorContentView();
    }

    private void collectTicksInfo() {
        try {
            if (mTicksCount < 0 || mTicksCount > 1000001) {
                throw new IllegalArgumentException("the Argument: TICK COUNT must be limited between (0-50), Now is " + mTicksCount);
            }
            if (mTicksCount != 0) {
                mTickMarksX = new float[mTicksCount];
                if (mShowTickText) {
                    mTextCenterX = new float[mTicksCount];
                    mTickTextsWidth = new float[mTicksCount];
                }
                mProgressArr = new float[mTicksCount];
                for (int i = 0; i < mProgressArr.length; i++) {
                    mProgressArr[i] = mMin + i * (mMax - mMin) / ((mTicksCount - 1) > 0 ? (mTicksCount - 1) : 1);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initDefaultPadding() {
        if (!mClearPadding) {
            int normalPadding = SizeUtils.vp2px(mContext, 16);
            if (getPaddingLeft() == 0) {
                setPadding(normalPadding, getPaddingTop(), getPaddingRight(), getPaddingBottom());
            }
            if (getPaddingRight() == 0) {
                setPadding(getPaddingLeft(), getPaddingTop(), normalPadding, getPaddingBottom());
            }
        }
    }

    private void initProgressRangeValue() {
        if (mMax < mMin) {
            throw new IllegalArgumentException("the Argument: MAX's value must be larger than MIN's.");
        }
        if (mProgress < mMin) {
            mProgress = mMin;
        }
        if (mProgress > mMax) {
            mProgress = mMax;
        }
    }

    private void initStrokePaint() {
        if (mStockPaint == null) {
            mStockPaint = new Paint();
        }
        if (mTrackRoundedCorners) {
            mStockPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        }
        mStockPaint.setAntiAlias(true);
        if (mBackgroundTrackSize > mProgressTrackSize) {
            mProgressTrackSize = mBackgroundTrackSize;
        }
    }

    private void measureTickTextsBonds() {
        if (needDrawText()) {
            initTextPaint();
            mTextPaint.setFont(mTextsTypeface);
            mRect = mTextPaint.getTextBounds("j");
            mTickTextsHeight = mRect.getHeight() + SizeUtils.vp2px(mContext, 3);//with the gap(3dp) between tickTexts and track.
        }
    }

    private boolean needDrawText() {
        return mShowThumbText || (mTicksCount != 0 && mShowTickText);
    }

    private void initTextPaint() {
        if (mTextPaint == null) {
            mTextPaint = new Paint();
            mTextPaint.setAntiAlias(true);
            mTextPaint.setTextAlign(TextAlignment.CENTER);
            mTextPaint.setTextSize(mTickTextsSize);
        }
        if (mRect == null) {
            mRect = new Rect();
        }
    }

    @Override
    public boolean onEstimateSize(int i, int i1) {
        int height = Math.round(mCustomDrawableMaxHeight + getPaddingTop() + getPaddingBottom());
        int width = resolveSize(SizeUtils.vp2px(mContext, 170), i) & MEASURED_SIZE_MASK;
        int height1 = height + mTickTextsHeight;
        setEstimatedSize(EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
            EstimateSpec.getChildSizeWithMode(height1, height1, EstimateSpec.NOT_EXCEED));
        initSeekBarInfo();
        refreshSeekBarLocation();
        if (mIndicatorStayAlways) {
            refreshSeekBar(getThumbCenterX());
        }
        return true;
    }

    @Override
    public void invalidate() {
        super.invalidate();
        Indicator indicator = getIndicator();
        if (indicator != null) {
            getIndicator().invalidate();
        }
    }

    public static final int MEASURED_SIZE_MASK = 0x00ffffff;
    public static final int MEASURED_STATE_TOO_SMALL = 0x01000000;
    public static final int MEASURED_STATE_MASK = 0xff000000;

    private int resolveSize(int size, int measureSpec) {
        final int specMode = EstimateSpec.getMode(measureSpec);
        final int specSize = EstimateSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case EstimateSpec.NOT_EXCEED:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case EstimateSpec.PRECISE:
                result = specSize;
                break;
            case EstimateSpec.UNCONSTRAINT:
            default:
                result = size;
        }
        return result;
    }

    private void initSeekBarInfo() {
        mMeasuredWidth = getEstimatedWidth() + getPaddingLeft() + getPaddingRight();
        mPaddingLeft = getPaddingLeft();
        mPaddingRight = getPaddingRight();
        mPaddingTop = getPaddingTop();
        mSeekLength = mMeasuredWidth - mPaddingLeft - mPaddingRight;

        mSeekBlockLength = mSeekLength / (mTicksCount - 1 > 0 ? mTicksCount - 1 : 1);

        System.out.println("LLM mSeekLength = " + mSeekLength + "  mSeekBlockLength = " + mSeekBlockLength);
    }

    private void refreshSeekBarLocation() {
        initTrackLocation();
        //init TickTexts Y Location
        if (needDrawText()) {
            mRect = mTextPaint.getTextBounds("j");
            mTickTextY = mPaddingTop + mCustomDrawableMaxHeight + Math.round(mRect.getHeight() - mTextPaint.descent()) + SizeUtils.vp2px(mContext, 3);
            mThumbTextY = mTickTextY;
        }
        //init tick's X and text's X location;
        if (mTickMarksX == null) {
            return;
        }
        initTextsArray();
        //adjust thumb auto,so find out the closest progress in the mProgressArr array and replace it.
        //it is not necessary to adjust thumb while count is less than 2.
        if (mTicksCount > 2) {
            mProgress = mProgressArr[getClosestIndex()];
            lastProgress = mProgress;
        }
        refreshThumbCenterXByProgress(mProgress);
    }

    private void initTextsArray() {
        if (mTicksCount == 0) {
            return;
        }
        if (mShowTickText) {
            mTickTextsArr = new String[mTicksCount];
        }
        for (int i = 0; i < mTickMarksX.length; i++) {
            if (mShowTickText) {
                mTickTextsArr[i] = getTickTextByPosition(i);
                mRect = mTextPaint.getTextBounds(mTickTextsArr[i]);
                mTickTextsWidth[i] = mRect.getWidth();
                mTextCenterX[i] = mPaddingLeft + mSeekBlockLength * i;
            }
            mTickMarksX[i] = mPaddingLeft + mSeekBlockLength * i;
        }
    }

    private void initTrackLocation() {
        if (mR2L) {
            System.out.println("LLM getAmplitude() =" + getAmplitude());
            mBackgroundTrack.left = mPaddingLeft;
            mBackgroundTrack.top = mPaddingTop + mThumbTouchRadius;
            //ThumbCenterX
            mBackgroundTrack.right = mPaddingLeft + mSeekLength * (1.0f - (mProgress - mMin) / (getAmplitude()));
            mBackgroundTrack.bottom = mBackgroundTrack.top;
            //ThumbCenterX
            mProgressTrack.left = mBackgroundTrack.right;
            mProgressTrack.top = mBackgroundTrack.top;
            mProgressTrack.right = mMeasuredWidth - mPaddingRight;
            mProgressTrack.bottom = mBackgroundTrack.bottom;
        } else {
            System.out.println("LLM getAmplitude() =" + getAmplitude());
            mProgressTrack.left = mPaddingLeft;
            mProgressTrack.top = mPaddingTop + mThumbTouchRadius;
            //ThumbCenterX
            mProgressTrack.right = (mProgress - mMin) * mSeekLength / (getAmplitude()) + mPaddingLeft;
            mProgressTrack.bottom = mProgressTrack.top;
            //ThumbCenterX
            mBackgroundTrack.left = mProgressTrack.right;
            mBackgroundTrack.top = mProgressTrack.bottom;
            mBackgroundTrack.right = mMeasuredWidth - mPaddingRight;
            mBackgroundTrack.bottom = mProgressTrack.bottom;
        }
    }

    private String getTickTextByPosition(int index) {
        if (mTickTextsCustomArray == null) {
            return getProgressString(mProgressArr[index]);
        }
        if (index < mTickTextsCustomArray.length) {
            return String.valueOf(mTickTextsCustomArray[index]);
        }
        return "";
    }

    /**
     * calculate the thumb's centerX by the changing progress.
     *
     * @param progress progress
     */
    private void refreshThumbCenterXByProgress(float progress) {
        //ThumbCenterX
        if (mR2L) {
            mBackgroundTrack.right = mPaddingLeft + mSeekLength * (1.0f - (progress - mMin) / (getAmplitude()));//ThumbCenterX
            mProgressTrack.left = mBackgroundTrack.right;
        } else {
            mProgressTrack.right = (progress - mMin) * mSeekLength / (getAmplitude()) + mPaddingLeft;
            mBackgroundTrack.left = mProgressTrack.right;
        }
    }

    private DrawTask drawTask = (component, canvas) -> {
        drawTrack(canvas);
        drawTickMarks(canvas);
        drawTickTexts(canvas);
        drawThumb(canvas);
        drawThumbText(canvas);
    };

    private void drawTrack(Canvas canvas) {
        if (mCustomTrackSectionColorResult) {//the track has custom the section track color
            int sectionSize = mTicksCount - 1 > 0 ? mTicksCount - 1 : 1;
            for (int i = 0; i < sectionSize; i++) {
                if (mR2L) {
                    mStockPaint.setColor(new Color(mSectionTrackColorArray[sectionSize - i - 1]));
                } else {
                    mStockPaint.setColor(new Color(mSectionTrackColorArray[i]));
                }
                float thumbPosFloat = getThumbPosOnTickFloat();
                if (i < thumbPosFloat && thumbPosFloat < (i + 1)) {
                    //the section track include the thumb,
                    // set the ProgressTrackSize for thumb's left side track ,
                    // BGTrackSize for the right's.
                    float thumbCenterX = getThumbCenterX();
                    mStockPaint.setStrokeWidth(getLeftSideTrackSize());
                    canvas.drawLine(new Point(mTickMarksX[i], mProgressTrack.top),
                        new Point(thumbCenterX, mProgressTrack.bottom), mStockPaint);
                    mStockPaint.setStrokeWidth(getRightSideTrackSize());
                    canvas.drawLine(new Point(thumbCenterX, mProgressTrack.top),
                        new Point(mTickMarksX[i + 1], mProgressTrack.bottom), mStockPaint);
                } else {
                    if (i < thumbPosFloat) {
                        mStockPaint.setStrokeWidth(getLeftSideTrackSize());
                    } else {
                        mStockPaint.setStrokeWidth(getRightSideTrackSize());
                    }
                    canvas.drawLine(new Point(mTickMarksX[i], mProgressTrack.top),
                        new Point(mTickMarksX[i + 1], mProgressTrack.bottom), mStockPaint);
                }
            }
        } else {
            //draw progress track
            mStockPaint.setColor(mProgressTrackColor);
            mStockPaint.setStrokeWidth(mProgressTrackSize);
            canvas.drawLine(new Point(mProgressTrack.left, mProgressTrack.top),
                new Point(mProgressTrack.right, mProgressTrack.bottom), mStockPaint);
            //draw BG track
            mStockPaint.setColor(mBackgroundTrackColor);
            mStockPaint.setStrokeWidth(mBackgroundTrackSize);
            canvas.drawLine(new Point(mBackgroundTrack.left, mBackgroundTrack.top),
                new Point(mBackgroundTrack.right, mBackgroundTrack.bottom), mStockPaint);
        }
    }

    private void drawTickMarks(Canvas canvas) {
        if (mTicksCount == 0 || (mShowTickMarksType == TickMarkType.NONE && mTickMarksDrawable == null)) {
            return;
        }
        float thumbCenterX = getThumbCenterX();
        for (int i = 0; i < mTickMarksX.length; i++) {
            float thumbPosFloat = getThumbPosOnTickFloat();
            if (mTickMarksSweptHide) {
                if (thumbCenterX >= mTickMarksX[i]) {
                    continue;
                }
            }
            if (mTickMarksEndsHide) {
                if (i == 0 || i == mTickMarksX.length - 1) {
                    continue;
                }
            }
            if (i == getThumbPosOnTick() && mTicksCount > 2 && !mSeekSmoothly) {
                continue;
            }
            if (i <= thumbPosFloat) {
                mStockPaint.setColor(new Color(getLeftSideTickColor()));
            } else {
                mStockPaint.setColor(new Color(getRightSideTickColor()));
            }
            if (mTickMarksDrawable != null) {
                if (mSelectTickMarksBitmap == null || mUnselectTickMarksBitmap == null) {
                    initTickMarksBitmap();
                }
                if (mSelectTickMarksBitmap == null || mUnselectTickMarksBitmap == null) {
                    //please check your selector drawable's format and correct.
                    throw new IllegalArgumentException("the format of the selector TickMarks drawable is wrong!");
                }
                if (i <= thumbPosFloat) {
                    if (mSelectTickMarksBitmap instanceof PixelMapElement) {
                        PixelMap pixelMap = ((PixelMapElement) mSelectTickMarksBitmap).getPixelMap();
                        canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),
                            mTickMarksX[i] - mSelectTickMarksBitmap.getWidth() / 2.0f,
                            mProgressTrack.top - mSelectTickMarksBitmap.getHeight() / 2.0f,
                            mStockPaint);
                    } else if (mSelectTickMarksBitmap instanceof ShapeElement) {
                        drawShapeElement(canvas, (ShapeElement) mSelectTickMarksBitmap, mTickMarksX[i], mProgressTrack.top, mTickRadius);
                    }
                } else {
                    if (mUnselectTickMarksBitmap instanceof PixelMapElement) {
                        PixelMap pixelMap = ((PixelMapElement) mUnselectTickMarksBitmap).getPixelMap();
                        canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),
                            mTickMarksX[i] - mUnselectTickMarksBitmap.getWidth() / 2.0f,
                            mProgressTrack.top - mUnselectTickMarksBitmap.getHeight() / 2.0f,
                            mStockPaint);
                    } else if (mUnselectTickMarksBitmap instanceof ShapeElement) {
                        drawShapeElement(canvas, (ShapeElement) mUnselectTickMarksBitmap, mTickMarksX[i], mProgressTrack.top, mTickRadius);
                    }
                }
                continue;
            }
            if (mShowTickMarksType == TickMarkType.OVAL) {
                canvas.drawCircle(mTickMarksX[i], mProgressTrack.top, mTickRadius, mStockPaint);
            } else if (mShowTickMarksType == TickMarkType.DIVIDER) {
                int rectWidth = SizeUtils.vp2px(mContext, 1);
                float dividerTickHeight;
                if (thumbCenterX >= mTickMarksX[i]) {
                    dividerTickHeight = getLeftSideTrackSize();
                } else {
                    dividerTickHeight = getRightSideTrackSize();
                }
                canvas.drawRect(mTickMarksX[i] - rectWidth, mProgressTrack.top - dividerTickHeight / 2.0f, mTickMarksX[i] + rectWidth, mProgressTrack.top + dividerTickHeight / 2.0f, mStockPaint);
            } else if (mShowTickMarksType == TickMarkType.SQUARE) {
                canvas.drawRect(mTickMarksX[i] - mTickMarksSize / 2.0f, mProgressTrack.top - mTickMarksSize / 2.0f, mTickMarksX[i] + mTickMarksSize / 2.0f, mProgressTrack.top + mTickMarksSize / 2.0f, mStockPaint);
            }
        }
    }

    private void drawTickTexts(Canvas canvas) {
        if (mTickTextsArr == null) {
            return;
        }
        float thumbPosFloat = getThumbPosOnTickFloat();
        for (int i = 0; i < mTickTextsArr.length; i++) {
            if (mShowBothTickTextsOnly) {
                if (i != 0 && i != mTickTextsArr.length - 1) {
                    continue;
                }
            }
            if (i == getThumbPosOnTick() && i == thumbPosFloat) {
                mTextPaint.setColor(new Color(mHoveredTextColor));
            } else if (i < thumbPosFloat) {
                mTextPaint.setColor(new Color(getLeftSideTickTextsColor()));
            } else {
                mTextPaint.setColor(new Color(getRightSideTickTextsColor()));
            }
            int index = i;
            if (mR2L) {
                index = mTickTextsArr.length - i - 1;
            }
            if (i == 0) {
                canvas.drawText(mTextPaint, mTickTextsArr[index], mTextCenterX[i] + mTickTextsWidth[index] / 2.0f, mTickTextY);
            } else if (i == mTickTextsArr.length - 1) {
                canvas.drawText(mTextPaint, mTickTextsArr[index], mTextCenterX[i] - mTickTextsWidth[index] / 2.0f, mTickTextY);
            } else {
                canvas.drawText(mTextPaint, mTickTextsArr[index], mTextCenterX[i], mTickTextY);
            }
        }
    }

    private void drawThumb(Canvas canvas) {
        if (mHideThumb) {
            return;
        }
        float thumbCenterX = getThumbCenterX();
        if (mThumbDrawable != null) {//check user has set thumb drawable or not.ThumbDrawable first, thumb color for later.
            if (mThumbBitmap == null || mPressedThumbBitmap == null) {
                initThumbBitmap();
            }
            if (mThumbBitmap == null || mPressedThumbBitmap == null) {
                //please check your selector drawable's format and correct.
                throw new IllegalArgumentException("the format of the selector thumb drawable is wrong!");
            }
            mStockPaint.setAlpha(1);
            if (mIsTouching) {
                if (mPressedThumbBitmap instanceof PixelMapElement) {
                    PixelMap pixelMap = ((PixelMapElement) mPressedThumbBitmap).getPixelMap();
                    canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),
                        thumbCenterX - pixelMap.getImageInfo().size.width / 2.0f,
                        mProgressTrack.top - pixelMap.getImageInfo().size.height / 2.0f,
                        mStockPaint);
                } else if (mPressedThumbBitmap instanceof ShapeElement) {
                    drawShapeElement(canvas, (ShapeElement) mPressedThumbBitmap, thumbCenterX, mProgressTrack.top, mThumbTouchRadius);
                }
            } else {
                if (mThumbBitmap instanceof PixelMapElement) {
                    PixelMap pixelMap = ((PixelMapElement) mThumbBitmap).getPixelMap();
                    canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),
                        thumbCenterX - pixelMap.getImageInfo().size.width / 2.0f,
                        mProgressTrack.top - pixelMap.getImageInfo().size.height / 2.0f,
                        mStockPaint);
                } else if (mThumbBitmap instanceof ShapeElement) {
                    drawShapeElement(canvas, (ShapeElement) mThumbBitmap, thumbCenterX, mProgressTrack.top, mThumbRadius);
                }
            }
        } else {
            if (mIsTouching) {
                mStockPaint.setColor(new Color(mPressedThumbColor));
            } else {
                mStockPaint.setColor(new Color(mThumbColor));
            }
            canvas.drawCircle(thumbCenterX, mProgressTrack.top, mIsTouching ? mThumbTouchRadius : mThumbRadius, mStockPaint);
        }
    }

    private void drawShapeElement(Canvas canvas, ShapeElement shapeElement, float x, float y, float radius) {
        Color color = new Color(shapeElement.getRgbColors()[0].asArgbInt());
        int shape = shapeElement.getShape();
        mStockPaint.setColor(color);
        int r = SizeUtils.vp2px(mContext, 7);
        switch (shape) {
            case ShapeElement.RECTANGLE:
                canvas.drawRect(x - r,
                    y - r,
                    x + r,
                    y + r,
                    mStockPaint);
                break;
            case ShapeElement.OVAL:
                int strokeWidth = shapeElement.getStrokeWidth();
                if (strokeWidth > 0) {
                    mStockPaint.setColor(new Color(shapeElement.getStrokeColor().asArgbInt()));
                    canvas.drawCircle(x, y, radius, mStockPaint);
                    mStockPaint.setColor(color);
                    canvas.drawCircle(x, y, radius - strokeWidth / 2.f, mStockPaint);
                } else {
                    canvas.drawCircle(x, y, r, mStockPaint);
                }
                break;
        }
    }

    private void drawThumbText(Canvas canvas) {
        if (!mShowThumbText || (mShowTickText && mTicksCount > 2)) {
            return;
        }
        mTextPaint.setColor(mThumbTextColor);
        canvas.drawText(mTextPaint, getProgressString(mProgress), getThumbCenterX(), mThumbTextY);
    }

    public float getThumbCenterX() {
        if (mR2L) {
            return mBackgroundTrack.right;
        }
        return mProgressTrack.right;
    }

    private int getLeftSideTickColor() {
        if (mR2L) {
            return mUnSelectedTickMarksColor;
        }
        return mSelectedTickMarksColor;
    }

    private int getRightSideTickColor() {
        if (mR2L) {
            return mSelectedTickMarksColor;
        }
        return mUnSelectedTickMarksColor;
    }

    private int getLeftSideTickTextsColor() {
        if (mR2L) {
            return mUnselectedTextsColor;
        }
        return mSelectedTextsColor;
    }

    private int getRightSideTickTextsColor() {
        if (mR2L) {
            return mSelectedTextsColor;
        }
        return mUnselectedTextsColor;
    }

    /**
     * get the track size which on the thumb left in R2L/L2R case.
     *
     * @return int
     */
    private int getLeftSideTrackSize() {
        if (mR2L) {
            return mBackgroundTrackSize;
        }
        return mProgressTrackSize;
    }

    /**
     * get the track size which on the thumb right in R2L/L2R case.
     *
     * @return int
     */
    private int getRightSideTrackSize() {
        if (mR2L) {
            return mProgressTrackSize;
        }
        return mBackgroundTrackSize;
    }

    private int getThumbPosOnTick() {
        if (mTicksCount != 0) {
            return Math.round((getThumbCenterX() - mPaddingLeft) / mSeekBlockLength);
        }
        //when tick count = 0 ; seek bar has not tick(continuous series), return 0;
        return 0;
    }

    private float getThumbPosOnTickFloat() {
        if (mTicksCount != 0) {
            return (getThumbCenterX() - mPaddingLeft) / mSeekBlockLength;
        }
        return 0;
    }

    private int getHeightByRatio(Element drawable, int width) {
        int intrinsicWidth = drawable.getWidth();
        int intrinsicHeight = drawable.getHeight();
        return Math.round(1.0f * width * intrinsicHeight / intrinsicWidth);
    }

    /**
     * initial the color for the thumb.
     * <p>
     * <p>
     * NOTICE: make sure the format of color selector you set is right.
     * int[][] states = colorStateList.getStates();
     * (1) if the states.length == 1,the way you set the thumb color like :
     * app:isb_thumb_color="#XXXXXX"  or
     * app:isb_thumb_color="@color/color_name" ;
     * <p>
     * (2) if the states.length == 3,the way you set the thumb color like :
     * app:isb_thumb_color="@color/selector_color_file_name". the file(located at res/color/)'s format should like:
     * <p>
     * (3) if the states.length == other, the color's format you set is not support.
     *
     * @param colorStateList colorStateList
     * @param defaultColor defaultColor
     */
    private void initThumbColor(StateElement colorStateList, int defaultColor) {
        //if you didn't set the thumb color, set a default color.
        if (colorStateList == null || colorStateList.getStateCount() < 1) {
            mThumbColor = defaultColor;
            mPressedThumbColor = mThumbColor;
            return;
        }

        int stateCount = colorStateList.getStateCount();
        if (stateCount == 1) {
            ShapeElement element = (ShapeElement) colorStateList.getStateElement(0).getCurrentElement();
            mThumbColor = element.getRgbColors()[0].asArgbInt();
            mPressedThumbColor = mThumbColor;
        } else if (stateCount == 2) {
            mThumbColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_PRESSED}).asArgbInt();
            mPressedThumbColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_EMPTY}).asArgbInt();
        } else {
            //the color selector file was set by a wrong format , please see above to correct.
            throw new IllegalArgumentException("the selector color file you set for the argument: isb_thumb_color is in wrong format.");
        }
    }

    /**
     * initial the color for the tick masks
     * <p>
     * <p>
     * NOTICE: make sure the format of color selector you set is right.
     * int[][] states = colorStateList.getStates();
     * (1) if the states.length == 1,the way you set the tick marks' color like :
     * app:isb_tick_marks_color="#XXXXXX"  or
     * app:isb_tick_marks_color="@color/color_name" ;
     * <p>
     * (2) if the states.length == 2,the way you set the tick marks' color like :
     * app:isb_tick_marks_color="@color/selector_color_file_name". the file(located at res/color/)'s format should like:
     * <p>
     * (3) if the states.length == other, the way you set is not support.
     *
     * @param colorStateList colorStateList
     * @param defaultColor defaultColor
     */
    private void initTickMarksColor(StateElement colorStateList, int defaultColor) {
        //if you didn't set the tick's text color, set a default selector color file.
        if (colorStateList == null || colorStateList.getStateCount() < 1) {
            mSelectedTickMarksColor = defaultColor;
            mUnSelectedTickMarksColor = mSelectedTickMarksColor;
            return;
        }

        int stateCount = colorStateList.getStateCount();
        if (stateCount == 1) {
            ShapeElement element = (ShapeElement) colorStateList.getStateElement(0).getCurrentElement();
            mSelectedTickMarksColor = element.getRgbColors()[0].asArgbInt();
            mUnSelectedTickMarksColor = mSelectedTickMarksColor;
        } else if (stateCount == 2) {
            mSelectedTickMarksColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_SELECTED}).asArgbInt();
            mUnSelectedTickMarksColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_EMPTY}).asArgbInt();
        } else {
            //the color selector file was set by a wrong format , please see above to correct.
            throw new IllegalArgumentException("the selector color file you set for the argument: isb_tick_marks_color is in wrong format.");
        }
    }

    /**
     * initial the color for the tick texts.
     * <p>
     * <p>
     * NOTICE: make sure the format of color selector you set is right.
     * int[][] states = colorStateList.getStates();
     * (1) if the states.length == 1,the way you set the tick texts' color like :
     * app:isb_tick_text_color="#XXXXXX"  or
     * app:isb_tick_text_color="@color/color_name" ;
     * <p>
     * (2) if the states.length == 3,the way you set the tick texts' color like :
     * app:isb_tick_text_color="@color/selector_color_file_name". the file(located at res/color/)'s format should like:
     * <p>
     * (3) if the states.length == other, the way you set is not support.
     *
     * @param colorStateList colorStateList
     * @param defaultColor defaultColor
     */
    private void initTickTextsColor(StateElement colorStateList, int defaultColor) {
        //if you didn't set the tick's texts color, will be set a selector color file default.
        if (colorStateList == null || colorStateList.getStateCount() < 1) {
            mUnselectedTextsColor = defaultColor;
            mSelectedTextsColor = mUnselectedTextsColor;
            mHoveredTextColor = mUnselectedTextsColor;
            return;
        }

        int stateCount = colorStateList.getStateCount();
        if (stateCount == 1) {
            ShapeElement element = (ShapeElement) colorStateList.getStateElement(0).getCurrentElement();
            mUnselectedTextsColor = element.getRgbColors()[0].asArgbInt();
            mSelectedTextsColor = mUnselectedTextsColor;
            mHoveredTextColor = mUnselectedTextsColor;
        } else if (stateCount == 3) {
            mHoveredTextColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_HOVERED}).asArgbInt();
            mSelectedTextsColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_SELECTED}).asArgbInt();
            mUnselectedTextsColor = getByState(colorStateList,
                new int[]{ComponentState.COMPONENT_STATE_EMPTY}).asArgbInt();
        } else {
            //the color selector file was set by a wrong format , please see above to correct.
            throw new IllegalArgumentException("the selector color file you set for the argument: isb_tick_texts_color is in wrong format.");
        }
    }

    /**
     * initial both the tick texts' and thumb text's typeface,just has 4 type to choose,
     * but you can set the CUSTOM typeface you want by java code.
     * <p>
     * usage like:
     * indicatorSeekbar.customTickTextsTypeface(Typeface.xxx);
     *
     * @param typeface typeface
     * @param defaultTypeface defaultTypeface
     */
    private void initTextsTypeface(int typeface, Font defaultTypeface) {
        switch (typeface) {
            case 0:
                mTextsTypeface = Font.DEFAULT;
                break;
            case 1:
                mTextsTypeface = Font.MONOSPACE;
                break;
            case 2:
                mTextsTypeface = Font.SANS_SERIF;
                break;
            case 3:
                mTextsTypeface = Font.SERIF;
                break;
            default:
                if (defaultTypeface == null) {
                    mTextsTypeface = Font.DEFAULT;
                    break;
                }
                mTextsTypeface = defaultTypeface;
                break;
        }
    }

    /**
     * initial the bitmap for the thumb.
     * <p>
     * <p>
     * NOTICE: make sure the format of drawable selector file you set is right.
     * int stateCount = listDrawable.getStateCount();
     * (1) if the drawable instanceof BitmapDrawable,the way you set like :
     * app:isb_thumb_drawable="@drawable/ic_launcher"
     * <p>
     * (2) if the drawable instanceof StateListDrawable,the way you set like :
     * app:isb_thumb_drawable="@drawable/selector_thumb_drawable". the file(located at res/drawable/)'s format should like:
     * <p>
     */
    private void initThumbBitmap() {
        if (mThumbDrawable == null) {
            return;
        }
        if (mThumbDrawable instanceof StateElement) {
            StateElement listDrawable = (StateElement) mThumbDrawable;
            int stateCount = listDrawable.getStateCount();
            if (stateCount == 2) {
                int selectedIndex = listDrawable.findStateElementIndex(new int[]{ComponentState.COMPONENT_STATE_PRESSED});
                if (selectedIndex < 0) {
                    throw new IllegalArgumentException("the state of the selector thumb drawable is wrong!");
                }
                mPressedThumbBitmap = listDrawable.getStateElement(selectedIndex);
                int emptyIndex = listDrawable.findStateElementIndex(new int[]{ComponentState.COMPONENT_STATE_EMPTY});
                if (emptyIndex < 0) {
                    throw new IllegalArgumentException("the state of the empty thumb drawable is wrong!");
                }
                mThumbBitmap = listDrawable.getStateElement(emptyIndex);
            } else {
                //please check your selector drawable's format, please see above to correct.
                throw new IllegalArgumentException("the format of the selector thumb drawable is wrong!");
            }
        } else {
            mThumbBitmap = mThumbDrawable;
            mPressedThumbBitmap = mThumbBitmap;
        }
    }

    /**
     * initial the bitmap for the thickMarks.
     * <p>
     * <p>
     * NOTICE: make sure the format of drawable selector file you set is right.
     * int stateCount = listDrawable.getStateCount();
     * (1) if the drawable instanceof BitmapDrawable,the way you set like :
     * app:isb_tick_marks_drawable="@drawable/ic_launcher"
     * <p>
     * (2) if the drawable instanceof StateListDrawable,the way you set like :
     * app:isb_tick_marks_drawable="@drawable/selector_thumb_drawable". the file(located at res/drawable/)'s format should like:
     * <p>
     */
    private void initTickMarksBitmap() {
        if (mTickMarksDrawable instanceof StateElement) {
            StateElement listDrawable = (StateElement) mTickMarksDrawable;
            int stateCount = listDrawable.getStateCount();
            if (stateCount == 2) {
                int selectedIndex = listDrawable.findStateElementIndex(new int[]{ComponentState.COMPONENT_STATE_SELECTED});
                if (selectedIndex < 0) {
                    throw new IllegalArgumentException("the state of the selector TickMarks drawable is wrong!");
                }
                mSelectTickMarksBitmap = listDrawable.getStateElement(selectedIndex);
                int emptyIndex = listDrawable.findStateElementIndex(new int[]{ComponentState.COMPONENT_STATE_EMPTY});
                if (emptyIndex < 0) {
                    throw new IllegalArgumentException("the state of the empty TickMarks drawable is wrong!");
                }
                mUnselectTickMarksBitmap = listDrawable.getStateElement(emptyIndex);
            } else {
                //please check your selector drawable's format, please see above to correct.
                throw new IllegalArgumentException("the format of the selector TickMarks drawable is wrong!");
            }
        } else {
            mUnselectTickMarksBitmap = mTickMarksDrawable;
            mSelectTickMarksBitmap = mUnselectTickMarksBitmap;
        }

    }

    @Override
    public void setEnabled(boolean enabled) {
        if (enabled == isEnabled()) {
            return;
        }
        super.setEnabled(enabled);
        if (isEnabled()) {
            setAlpha(1.0f);
            if (mIndicatorStayAlways) {
                mIndicatorContentView.setAlpha(1.0f);
            }
        } else {
            setAlpha(0.3f);
            if (mIndicatorStayAlways) {
                mIndicatorContentView.setAlpha(0.3f);
            }
        }
    }

    @Override
    public boolean simulateClick() {
        return super.simulateClick();
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        if (!mUserSeekable || !isEnabled()) {
            return false;
        }
        float x = TouchHelper.getLocalX(this, event);
        float y = TouchHelper.getLocalY(this, event);
        switch (event.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                simulateClick();
                float mX = x;
                if (isTouchSeekBar(mX, y)) {
                    if ((mOnlyThumbDraggable && !isTouchThumb(mX))) {
                        return false;
                    }
                    mIsTouching = true;
                    if (mOnSeekBarChangeListener != null) {
                        mOnSeekBarChangeListener.onStartTrackingTouch(this);
                    }
                    refreshSeekBar(x);
                    return true;
                }
                break;
            case TouchEvent.POINT_MOVE:
                refreshSeekBar(x);
                break;
            case TouchEvent.PRIMARY_POINT_UP:
            case TouchEvent.CANCEL:
                mIsTouching = false;
                if (mOnSeekBarChangeListener != null) {
                    mOnSeekBarChangeListener.onStopTrackingTouch(this);
                }
                if (!autoAdjustThumb()) {
                    invalidate();
                }
                if (mIndicator != null) {
                    mIndicator.hide();
                }
                break;
        }
        return false;
    }

    public void refreshSeekBar(float x) {
        refreshThumbCenterXByProgress(calculateProgress(calculateTouchX(adjustTouchX(x))));
        setSeekListener(true);
        invalidate();
        updateIndicator();
    }

    private boolean progressChange() {
        if (mIsFloatProgress) {
            return lastProgress != mProgress;
        } else {
            return Math.round(lastProgress) != Math.round(mProgress);
        }
    }

    private float adjustTouchX(float x) {
        float mTouchXCache;
        if (x < mPaddingLeft) {
            mTouchXCache = mPaddingLeft;
        } else if (x > mMeasuredWidth - mPaddingRight) {
            mTouchXCache = mMeasuredWidth - mPaddingRight;
        } else {
            mTouchXCache = x;
        }
        return mTouchXCache;
    }

    private float calculateProgress(float touchX) {
        lastProgress = mProgress;
        mProgress = mMin + (getAmplitude()) * (touchX - mPaddingLeft) / mSeekLength;
        return mProgress;
    }

    private float calculateTouchX(float touchX) {
        float touchXTemp = touchX;
        //make sure the seek bar to seek smoothly always
        // while the tick's count is less than 3(tick's count is 1 or 2.).
        if (mTicksCount > 2 && !mSeekSmoothly) {
            int touchBlockSize = Math.round((touchX - mPaddingLeft) / mSeekBlockLength);
            touchXTemp = mSeekBlockLength * touchBlockSize + mPaddingLeft;
        }
        if (mR2L) {
            return mSeekLength - touchXTemp + 2 * mPaddingLeft;
        }
        return touchXTemp;
    }

    private boolean isTouchSeekBar(float mX, float mY) {
        if (mFaultTolerance == -1) {
            mFaultTolerance = SizeUtils.vp2px(mContext, 5);
        }
        boolean inWidthRange = mX >= (mPaddingLeft - 2 * mFaultTolerance) && mX <= (mMeasuredWidth - mPaddingRight + 2 * mFaultTolerance);
        boolean inHeightRange = mY >= mProgressTrack.top - mThumbTouchRadius - mFaultTolerance && mY <= mProgressTrack.top + mThumbTouchRadius + mFaultTolerance;
        return inWidthRange && inHeightRange;
    }

    private boolean isTouchThumb(float mX) {
        float rawTouchX;
        refreshThumbCenterXByProgress(mProgress);
        if (mR2L) {
            rawTouchX = mBackgroundTrack.right;
        } else {
            rawTouchX = mProgressTrack.right;
        }
        return rawTouchX - mThumbSize / 2f <= mX && mX <= rawTouchX + mThumbSize / 2f;
    }

    public void updateIndicator() {
        if (mIndicatorStayAlways) {
            updateStayIndicator();
        } else {
            if (mIndicator == null) {
                return;
            }
            mIndicator.iniPop();
            if (mIndicator.isShowing()) {
                mIndicator.update(getThumbCenterX());
            } else {
                mIndicator.show(getThumbCenterX());
            }
        }
    }

    private void initIndicatorContentView() {
        if (mShowIndicatorType == IndicatorType.NONE) {
            return;
        }
        if (mIndicator == null) {
            mIndicator = new Indicator(mContext,
                this,
                mIndicatorColor,
                mShowIndicatorType,
                mIndicatorTextSize,
                mIndicatorBoolen,
                mIndicatorTextColor,
                mIndicatorContentView,
                mIndicatorTopContentView);
            this.mIndicatorContentView = mIndicator.getInsideContentView();
        }
    }

    public void updateStayIndicator() {
        if (!mIndicatorStayAlways || mIndicator == null) {
            return;
        }
        mIndicator.setProgressTextView(getIndicatorTextString());
        mIndicatorContentView.estimateSize(0, 0);
        int measuredWidth = mIndicatorContentView.getEstimatedWidth();
        float thumbCenterX = getThumbCenterX();

        if (mScreenWidth == -1) {
            DisplayManager instance = DisplayManager.getInstance();
            Display display = instance.getDefaultDisplay(mContext).get();
            mScreenWidth = display.getAttributes().width;
        }
        int indicatorOffset;
        int arrowOffset;
        int temp =measuredWidth / 2 + (int)thumbCenterX;
        if (BigDecimal.valueOf(temp).compareTo(BigDecimal.valueOf(mMeasuredWidth)) == 1) {
            indicatorOffset = mMeasuredWidth - measuredWidth;
            arrowOffset = (int) ((int)thumbCenterX - indicatorOffset - measuredWidth / 2.f);
        } else if (thumbCenterX - measuredWidth / 2.f < 0) {
            indicatorOffset = 0;
            arrowOffset = -(int) (measuredWidth / 2.f - thumbCenterX);
        } else {
            indicatorOffset = (int) (getThumbCenterX() - measuredWidth / 2.f);
            arrowOffset = 0;
        }

        mIndicator.updateIndicatorLocation(indicatorOffset);
        mIndicator.updateArrowViewLocation(arrowOffset);
    }

    private boolean autoAdjustThumb() {
        if (mTicksCount < 3 || !mSeekSmoothly) {//it is not necessary to adjust while count less than 2.
            return false;
        }
        if (!mAdjustAuto) {
            return false;
        }
        final int closestIndex = getClosestIndex();
        final float touchUpProgress = mProgress;
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                float abs = Math.abs(touchUpProgress - mProgressArr[closestIndex]);
                lastProgress = mProgress;
                if (touchUpProgress - mProgressArr[closestIndex] > 0) {
                    mProgress = touchUpProgress - (abs * v);
                } else {
                    mProgress = touchUpProgress + (abs * v);
                }
                refreshThumbCenterXByProgress(mProgress);
                //the auto adjust was happened after user touched up, so from user is false.
                setSeekListener(false);
                if (mIndicator != null && mIndicatorStayAlways) {
                    mIndicator.refreshProgressText();
                    updateStayIndicator();
                }
                invalidate();
            }
        });
        animatorValue.start();
        return true;
    }

    /**
     * transfer the progress value to string type
     *
     * @param progress progress
     * @return String
     */
    private String getProgressString(float progress) {
        if (mIsFloatProgress) {
            return FormatUtils.fastFormat(progress, mScale);
        } else {
            return String.valueOf(Math.round(progress));
        }
    }

    private int getClosestIndex() {
        int closestIndex = 0;
        float amplitude = Math.abs(mMax - mMin);
        for (int i = 0; i < mProgressArr.length; i++) {
            float amplitudeTemp = Math.abs(mProgressArr[i] - mProgress);
            if (amplitudeTemp <= amplitude) {
                amplitude = amplitudeTemp;
                closestIndex = i;
            }
        }
        return closestIndex;
    }

    private float getAmplitude() {
        return (mMax - mMin) > 0 ? (mMax - mMin) : 1;
    }

    private void setSeekListener(boolean formUser) {
        if (mOnSeekBarChangeListener == null) {
            return;
        }
        if (progressChange()) {
            mOnSeekBarChangeListener.onProgressChanged(collectParams(formUser));
        }
    }

    private SeekParams collectParams(boolean formUser) {
        if (mSeekParams == null) {
            mSeekParams = new SeekParams(this);
        }
        mSeekParams.progress = getProgress();
        mSeekParams.progressFloat = getProgressFloat();
        mSeekParams.fromUser = formUser;
        //for discrete series seek bar
        if (mTicksCount > 2) {
            int rawThumbPos = getThumbPosOnTick();
            if (mShowTickText && mTickTextsArr != null) {
                mSeekParams.tickText = mTickTextsArr[rawThumbPos];
            }
            if (mR2L) {
                mSeekParams.thumbPosition = mTicksCount - rawThumbPos - 1;
            } else {
                mSeekParams.thumbPosition = rawThumbPos;
            }
        }
        return mSeekParams;
    }

    private void apply(Builder builder) {
        //seek bar
        this.mMax = builder.max;
        this.mMin = builder.min;
        this.mProgress = builder.progress;
        this.mIsFloatProgress = builder.progressValueFloat;
        this.mTicksCount = builder.tickCount;
        this.mSeekSmoothly = builder.seekSmoothly;
        this.mR2L = builder.r2l;
        this.mUserSeekable = builder.userSeekable;
        this.mClearPadding = builder.clearPadding;
        this.mOnlyThumbDraggable = builder.onlyThumbDraggable;
        //indicator
        this.mShowIndicatorType = builder.showIndicatorType;
        this.mIndicatorColor = builder.indicatorColor;
        this.mIndicatorTextColor = builder.indicatorTextColor;
        this.mIndicatorTextSize = builder.indicatorTextSize;
        this.mIndicatorContentView = builder.indicatorContentView;
        this.mIndicatorTopContentView = builder.indicatorTopContentView;
        //track
        this.mBackgroundTrackSize = builder.trackBackgroundSize;
        this.mBackgroundTrackColor = builder.trackBackgroundColor;
        this.mProgressTrackSize = builder.trackProgressSize;
        this.mProgressTrackColor = builder.trackProgressColor;
        this.mTrackRoundedCorners = builder.trackRoundedCorners;
        //thumb
        this.mThumbSize = builder.thumbSize;
        this.mThumbDrawable = builder.thumbDrawable;
        this.mThumbTextColor = builder.thumbTextColor;
        initThumbColor(builder.thumbColorStateList, builder.thumbColor);
        this.mShowThumbText = builder.showThumbText;
        //tickMarks
        this.mShowTickMarksType = builder.showTickMarksType;
        this.mTickMarksSize = builder.tickMarksSize;
        this.mTickMarksDrawable = builder.tickMarksDrawable;
        this.mTickMarksEndsHide = builder.tickMarksEndsHide;
        this.mTickMarksSweptHide = builder.tickMarksSweptHide;
        initTickMarksColor(builder.tickMarksColorStateList, builder.tickMarksColor);
        //tickTexts
        this.mShowTickText = builder.showTickText;
        this.mTickTextsSize = builder.tickTextsSize;
        this.mTickTextsCustomArray = builder.tickTextsCustomArray;
        this.mTextsTypeface = builder.tickTextsTypeFace;
        initTickTextsColor(builder.tickTextsColorStateList, builder.tickTextsColor);
    }

    private AnimatorValue animatorValue;

    /**
     * first showing when initial for indicator stay always.
     */
    public void showStayIndicator() {
        mIndicatorContentView.setVisibility(INVISIBLE);
        animatorValue = new AnimatorValue();
        animatorValue.setDuration(500);
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                if (v > 0.6) {
                    if (mIndicatorContentView.getVisibility() != VISIBLE) {
                        mIndicatorContentView.setVisibility(VISIBLE);
                    }
                    mIndicatorContentView.setAlpha((v - 0.6f) / 0.4f);
                }
            }
        });
        animatorValue.start();
        updateStayIndicator();
    }

    /**
     * IndicatorStayLayout call this, always true.
     *
     * @param indicatorStayAlways IndicatorStayLayout call this, always true.
     */
    public void setIndicatorStayAlways(boolean indicatorStayAlways) {
        this.mIndicatorStayAlways = indicatorStayAlways;
    }

    public Component getIndicatorContentView() {
        return mIndicatorContentView;
    }

    public void setIndicatorContentView(Component contentView) {
        this.mIndicatorContentView = contentView;
        getIndicator().setIndicatorContentView(contentView);
    }

    public String getIndicatorTextString() {
        if (mIndicatorTextFormat != null && mIndicatorTextFormat.contains(FORMAT_TICK_TEXT)) {
            if (mTicksCount > 2 && mTickTextsArr != null) {
                return mIndicatorTextFormat.replace(FORMAT_TICK_TEXT, mTickTextsArr[getThumbPosOnTick()]);
            }
        } else if (mIndicatorTextFormat != null && mIndicatorTextFormat.contains(FORMAT_PROGRESS)) {
            return mIndicatorTextFormat.replace(FORMAT_PROGRESS, getProgressString(mProgress));
        }
        return getProgressString(mProgress);
    }

    // 读取选择器：输入状态集，输出 Element(或Element包裹的颜色值)
    private RgbColor getByState(StateElement stateElement, int[] stateSet) {
        // 查找相应状态 element 的 index
        int index = stateElement.findStateElementIndex(stateSet);
        if (index < 0) {
            throw new IllegalArgumentException("stateSet not found!");
        }

        // 根据 index 取出 Element (这里是要取出颜色值，所以强转为 ShapeElement 再取)
        ShapeElement element = (ShapeElement) stateElement.getStateElement(index).getCurrentElement();
        return element.getRgbColors()[0];
    }

    /*------------------API START-------------------*/

    /**
     * call this to new a builder with default params.
     *
     * @param context context environment
     * @return Builder
     */
    public static Builder with(Context context) {
        return new Builder(context);
    }

    /**
     * get current indicator
     *
     * @return the indicator
     */
    public Indicator getIndicator() {
        return mIndicator;
    }

    /**
     * get the tick' count
     *
     * @return tick' count
     */
    public int getTickCount() {
        return mTicksCount;
    }

    /**
     * Get the seek bar's current level of progress in float type.
     *
     * @return current progress in float type.
     */
    public float getProgressFloat() {
        BigDecimal bigDecimal = BigDecimal.valueOf(mProgress);
        return bigDecimal.setScale(mScale, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    /**
     * Get the seek bar's current level of progress in int type.
     *
     * @return progress in int type.
     */
    public int getProgress() {
        return Math.round(mProgress);
    }

    /**
     * the upper limit of this seek bar's range.
     *
     * @return the upper limit of this seek bar's range.
     */
    public float getMax() {
        return mMax;
    }

    /**
     * the lower limit of this seek bar's range.
     *
     * @return the seek bar min value
     */
    public float getMin() {
        return mMin;
    }

    /**
     * the listener to listen the seeking params changing.
     *
     * @return seeking listener.
     */
    public OnSeekBarChangeListener getOnSeekChangeListener() {
        return mOnSeekBarChangeListener;
    }

    /**
     * Sets the current progress to the specified value.also,
     * if the seek bar's tick'count is larger than 2,the progress will adjust to the closest tick's progress auto.
     *
     * @param progress a new progress value , if the new progress is less than min ,
     * it will set to min;
     * if over max ,will be max.
     */
    public void setProgress(float progress) {
        lastProgress = mProgress;
        mProgress = progress < mMin ? mMin : (progress > mMax ? mMax : progress);
        //adjust to the closest tick's progress
        if ((!mSeekSmoothly) && mTicksCount > 2) {
            mProgress = mProgressArr[getClosestIndex()];
        }
        setSeekListener(false);
        refreshThumbCenterXByProgress(mProgress);
        invalidate();
        updateStayIndicator();
    }

    /**
     * Set the upper range of the seek bar
     *
     * @param max the upper range of this progress bar.
     */
    public void setMax(float max) {
        this.mMax = Math.max(mMin, max);
        initProgressRangeValue();
        collectTicksInfo();
        refreshSeekBarLocation();
        invalidate();
        updateStayIndicator();
    }

    /**
     * Set the min value for SeekBar
     *
     * @param min the min value , if is larger than max, will set to max.
     */
    public void setMin(float min) {
        this.mMin = Math.min(mMax, min);
        initProgressRangeValue();
        collectTicksInfo();
        refreshSeekBarLocation();
        invalidate();
        updateStayIndicator();
    }

    /**
     * compat app local change
     *
     * @param isR2L True if see form right to left on the screen.
     */
    public void setR2L(boolean isR2L) {
        this.mR2L = isR2L;
        postLayout();
        invalidate();
        updateStayIndicator();
    }

    /**
     * Set a new thumb drawable.
     *
     * @param drawable the drawable for thumb,selector drawable is ok.
     * selector format:
     */
    public void setThumbDrawable(Element drawable) {
        if (drawable == null) {
            this.mThumbDrawable = null;
            this.mThumbBitmap = null;
            this.mPressedThumbBitmap = null;
        } else {
            this.mThumbDrawable = drawable;
            this.mThumbRadius = Math.min(SizeUtils.vp2px(mContext, THUMB_MAX_WIDTH), mThumbSize) / 2.0f;
            this.mThumbTouchRadius = mThumbRadius;
            this.mCustomDrawableMaxHeight = Math.max(mThumbTouchRadius, mTickRadius) * 2.0f;
            initThumbBitmap();
        }
        postLayout();
        invalidate();
    }

    /**
     * call this will do not draw thumb, true if hide.
     *
     * @param hide hide
     */
    public void hideThumb(boolean hide) {
        mHideThumb = hide;
        invalidate();
    }

    /**
     * call this will do not draw the text which below thumb. true if hide.
     *
     * @param hide hide
     */
    public void hideThumbText(boolean hide) {
        mShowThumbText = !hide;
        invalidate();
    }

    /**
     * set the seek bar's thumb's color.
     *
     * @param thumbColor colorInt
     */
    public void thumbColor(int thumbColor) {
        this.mThumbColor = thumbColor;
        this.mPressedThumbColor = thumbColor;
        invalidate();
    }

    /**
     * set the seek bar's thumb's selector color.
     *
     * @param thumbColorStateList color selector
     * selector format like:
     */
    public void thumbColorStateList(StateElement thumbColorStateList) {
        initThumbColor(thumbColorStateList, mThumbColor);
        invalidate();
    }

    /**
     * Set a new tick marks drawable.
     *
     * @param drawable the drawable for marks,selector drawable is ok.
     * selector format:
     */
    public void setTickMarksDrawable(Element drawable) {
        if (drawable == null) {
            this.mTickMarksDrawable = null;
            this.mUnselectTickMarksBitmap = null;
            this.mSelectTickMarksBitmap = null;
        } else {
            this.mTickMarksDrawable = drawable;
            this.mTickRadius = Math.min(SizeUtils.vp2px(mContext, THUMB_MAX_WIDTH), mTickMarksSize) / 2.0f;
            this.mCustomDrawableMaxHeight = Math.max(mThumbTouchRadius, mTickRadius) * 2.0f;
            initTickMarksBitmap();
        }
        invalidate();
    }

    /**
     * set the seek bar's tick's color.
     *
     * @param tickMarksColor colorInt
     */
    public void tickMarksColor(int tickMarksColor) {
        this.mSelectedTickMarksColor = tickMarksColor;
        this.mUnSelectedTickMarksColor = tickMarksColor;
        invalidate();
    }

    /**
     * set the seek bar's tick's color.
     *
     * @param tickMarksColorStateList colorInt
     * selector format like:
     */
    public void tickMarksColor(StateElement tickMarksColorStateList) {
        initTickMarksColor(tickMarksColorStateList, mSelectedTickMarksColor);
        invalidate();
    }

    /**
     * set the color for text below/above seek bar's tickText.
     *
     * @param tickTextsColor ColorInt
     */
    public void tickTextsColor(int tickTextsColor) {
        mUnselectedTextsColor = tickTextsColor;
        mSelectedTextsColor = tickTextsColor;
        mHoveredTextColor = tickTextsColor;
        invalidate();
    }

    /**
     * set the selector color for text below/above seek bar's tickText.
     *
     * @param tickTextsColorStateList ColorInt
     * selector format like:
     */
    public void tickTextsColorStateList(StateElement tickTextsColorStateList) {
        initTickTextsColor(tickTextsColorStateList, mSelectedTextsColor);
        invalidate();
    }

    /**
     * The specified scale for the progress value,
     * make sure you had chosen the float progress type
     * <p>
     * such as:
     * scale = 3; progress: 1.78627347 to 1.786
     * scale = 4; progress: 1.78627347 to 1.7863
     * <p>
     * make sure you have call the attr progress_value_float=true before, otherwise no change.
     *
     * @param scale scale for the float type progress value.
     */
    public void setDecimalScale(int scale) {
        this.mScale = scale;
    }

    /**
     * Set a format string with placeholder ${PROGRESS} or ${TICK_TEXT} to IndicatorSeekBar,
     * the indicator's text would change.
     * For example:
     * seekBar.setIndicatorTextFormat("${PROGRESS} %");
     * seekBar.setIndicatorTextFormat("${PROGRESS} miles");
     * seekBar.setIndicatorTextFormat("I am ${TICK_TEXT}%");
     * <p>
     * make sure you have custom and show the tick text before you
     * use ${TICK_TEXT}% , otherwise will be shown a "" value.
     * <p>
     * Also, if the SeekBar type is Custom ,this method will be no work, see{@link IndicatorType}
     *
     * @param format the format for indicator text
     */
    public void setIndicatorTextFormat(String format) {
        this.mIndicatorTextFormat = format;
        initTextsArray();
        updateStayIndicator();
    }

    /**
     * Collect and custom the color for each of section track.
     * <p>
     * usage :
     * <p>
     * indicatorSeekBar.customSectionTrackColor(new ColorCollector() {
     * <p>
     * public boolean collectSectionTrackColor(int[] colorIntArr) {
     * colorIntArr[0] = getResources().getColor(R.color.color_blue);
     * colorIntArr[1] = getResources().getColor(R.color.color_gray);
     * colorIntArr[2] = Color.parseColor("#FFFF00");
     * ......
     * return true; // True if apply color , otherwise no change.
     * }
     * });
     *
     * @param collector The container for section track's color
     */
    public void customSectionTrackColor(ColorCollector collector) {
        int[] colorArray = new int[mTicksCount - 1 > 0 ? mTicksCount - 1 : 1];
        for (int i = 0; i < colorArray.length; i++) {
            //set the default section color
            colorArray[i] = mBackgroundTrackColor.getValue();
        }
        this.mCustomTrackSectionColorResult = collector.collectSectionTrackColor(colorArray);
        this.mSectionTrackColorArray = colorArray;
        invalidate();
    }

    /**
     * Replace the number ticks' texts with your's by String[].
     * Usually, the text array's length your set should equals seek bar's tickMarks' count.
     *
     * @param tickTextsArr The array contains the tick text
     */
//    public void customTickTexts(String[] tickTextsArr) {
//        this.mTickTextsCustomArray = tickTextsArr;
//        if (mTickTextsArr != null) {
//            for (int i = 0; i < mTickTextsArr.length; i++) {
//                String tickText;
//                if (i < tickTextsArr.length) {
//                    tickText = String.valueOf(tickTextsArr[i]);
//                } else {
//                    tickText = "";
//                }
//                int index = i;
//                if (mR2L) {
//                    index = mTicksCount - 1 - i;
//                }
//                mTickTextsArr[index] = tickText;
//                if (mTextPaint != null && mRect != null) {
//                    mRect = mTextPaint.getTextBounds(tickText);
//                    mTickTextsWidth[index] = mRect.getWidth();
//                }
//            }
//            invalidate();
//        }
//    }

    /**
     * Set the custom tick texts' typeface you want.
     *
     * @param typeface The typeface for tickTexts.
     */
    public void customTickTextsTypeface(Font typeface) {
        this.mTextsTypeface = typeface;
        measureTickTextsBonds();
        postLayout();
        invalidate();
    }


    /**
     * only show the tick texts on both of ends seek bar, make sure you hava called the attr:show tick text before.
     *
     * @param onlyShow true if only show the tick texts on both of ends seek bar
     */
    public void showBothEndsTickTextsOnly(boolean onlyShow) {
        this.mShowBothTickTextsOnly = onlyShow;
    }

    /**
     * prevent user from seeking
     *
     * @param seekAble true if user can seek
     */
    public void setUserSeekAble(boolean seekAble) {
        this.mUserSeekable = seekAble;
    }

    public void setShowIndicatorType(int indicatorType) {
        this.mShowIndicatorType = indicatorType;
        getIndicator().setIndicatorType(indicatorType);
    }

    /**
     * Sets the tick count
     *
     * @param tickCount
     */
    public void setTickCount(int tickCount) {
        if (mTicksCount < 0 || mTicksCount > 1000001) {
            throw new IllegalArgumentException("the Argument: TICK COUNT must be limited between (0-50), Now is " + mTicksCount);
        }
        System.out.println("tickCount = " + tickCount);
        mTicksCount = tickCount;
        collectTicksInfo();
        initTextsArray();
        initSeekBarInfo();
        refreshSeekBarLocation();

        invalidate();
        updateStayIndicator();

    }

    /**
     * Sets the thumb move to the closed tick after touched up automatically, default true
     *
     * @param adjustAuto true if auto move after touched up.
     */
    public void setThumbAdjustAuto(boolean adjustAuto) {
        mAdjustAuto = adjustAuto;
    }


    /*------------------API END-------------------*/


    @Override
    public void onDragDown(Component component, DragInfo dragInfo) {

    }

    @Override
    public void onDragStart(Component component, DragInfo dragInfo) {

    }

    @Override
    public void onDragUpdate(Component component, DragInfo dragInfo) {

    }

    @Override
    public void onDragEnd(Component component, DragInfo dragInfo) {

    }

    @Override
    public void onDragCancel(Component component, DragInfo dragInfo) {

    }

    @Override
    public boolean onDragPreAccept(Component component, int dragDirection) {
        return true;
    }

}